package com.alcatel_lucent.osp.service;

import com.alcatel_lucent.osp.BusinessException;
import com.alcatel_lucent.osp.action.UserContext;
import com.alcatel_lucent.osp.model.*;
import com.alcatel_lucent.osp.utils.DateUtil;
import net.sf.json.JSONObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Transformer;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.json.JSONException;
import org.apache.struts2.json.JSONUtil;
import org.intellij.lang.annotations.Language;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;

@Service
public class RequestServiceImpl extends BaseServiceImpl implements
        RequestService {
    private static Logger log = LoggerFactory.getLogger(RequestServiceImpl.class);


    private NotificationService notificationService;

    @Autowired
    private UserService userService;

    @Value("${rd360.category}")
    private String rd360Category;

    private String webRoot=".";

    public String getWebRoot() {
        return webRoot;
    }

    public void setWebRoot(String webRoot) {
        this.webRoot = webRoot;
        this.notificationService.setWebRoot(webRoot);
    }

    public static final Map<String, String> rd360ProgramTypeMapping = new HashMap<String, String>();

    {
        rd360ProgramTypeMapping.put("Program", "PCT");
        rd360ProgramTypeMapping.put("OEM Program", "OEM");
        rd360ProgramTypeMapping.put("Support Program", "SUPPORT_PCT");
        rd360ProgramTypeMapping.put("Offer", "OFFER");
    }

    public NotificationService getNotificationService() {
        return notificationService;
    }

    @Autowired
    public void setNotificationService(NotificationService notificationService) {
        this.notificationService = notificationService;
    }


    @Override
    public Request createRequest(Request request) {
        Timestamp now = new Timestamp(System.currentTimeMillis());

        User reportBy = getCurrentUser();
//        request.setReportBy(reportBy);

        request.setLastUpdateBy(reportBy);
        request.setReportTime(now);
        request.setLastUpdateTime(now);
        request.setCategory((Category) dao.retrieve(Category.class, request.getCategory().getId()));
        request.setType((Type) dao.retrieve(Type.class, request.getType().getId()));
        request = (Request) dao.create(request);
        if (request.getCategory().getName().equalsIgnoreCase(rd360Category)) {
            notificationService.notifyNewRd360Request(request);
        } else {
            notificationService.notifyNewRequest(request);
        }

        return request;
    }

    @Override
    public Collection<Request> findMyRequest(User user) {
        if (null == user) return CollectionUtils.EMPTY_COLLECTION;
        Map<String, User> params = new HashMap<String, User>();
        params.put("reportBy", user);
        @Language("HQL") String hql = "from Request where reportBy=:reportBy ORDER BY reportTime DESC";
        return dao.retrieve(hql, params);
    }

    @Override
    public Collection<Request> findMyFollowups(User user) {
        if (null == user) return CollectionUtils.EMPTY_COLLECTION;
        Map<String, User> params = new HashMap<String, User>();
        params.put("owner", user);
        @Language("HQL") String hql = "from Request r where exists (from r.category.owners ro where ro.csl = :owner) order by r.reportTime desc";
        return dao.retrieve(hql, params);
    }

    @Override
    public Request findRequestById(Long id) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("id", id);
        return (Request) dao.retrieveOne("from Request where id = :id", params,
                new String[]{"comments", "attachments", "type", "lastUpdateBy", "reportBy", "copyList",
                        "category", "category.owners", "category.types",
                }
        );
    }

    public String getRequestJSONById(Long id, Collection<String> excludeProperties) throws JSONException {
        return getRequestJSON((Request) dao.retrieve(Request.class, id), excludeProperties);
    }

    public String getRequestJSON(Request request, Collection<String> excludeProperties) throws JSONException {
        Collections.sort((List<Comment>) request.getComments(), new Comparator<Comment>() {
            @Override
            public int compare(Comment c1, Comment c2) {
                return (int) (c2.getCreateTime().getTime() - c1.getCreateTime().getTime());
            }
        });

        Collection<Pattern> excludePropertyPatterns = CollectionUtils.collect(excludeProperties, new Transformer() {
            @Override
            public Object transform(Object input) {
                return Pattern.compile((String) input);
            }
        });

        String strJson = JSONUtil.serialize(request, excludePropertyPatterns, null, false, true);
        strJson = JSONObject.fromObject(strJson).toString(2);
        return strJson;
    }

    @Override
    public Comment createComment(Long requestId, String content) {
        Request request = (Request) dao.retrieve(Request.class, requestId);
        User user = getCurrentUser();
        Comment comment = new Comment();
        comment.setRequest(request);
        comment.setCreator(user);
        comment.setCreateTime(new Timestamp(System.currentTimeMillis()));
        comment.setContent(content);
        comment = (Comment) dao.create(comment);
        return comment;
    }


    @Override
    public Request updateRequest(Long requestId, RequestStatus status, Date targetDate, String comment, String[] copyList) {
        Request request = (Request) dao.retrieve(Request.class, requestId);
        request.setCopyList(userService.getAndSaveUsersByCSL(copyList));

        if (isGuestAccess(request)) {
            throw new BusinessException(BusinessException.ACCESS_DENIED);
        }

        User user = getCurrentUser();
        boolean isOwner = request.isOwner(user);
        String changes = "";
        if (status != request.getStatus()) {
            if (!isOwner && status != RequestStatus.CLOSED) {
                throw new BusinessException(BusinessException.ACCESS_DENIED);
            }
            changes = "Status -> " + status.toString() + "\n";
            request.setStatus(status);
        }
        if (targetDate != null) {
            targetDate = DateUtil.roundDate(targetDate);
            if (request.getTargetDate() == null || request.getTargetDate().getTime() != targetDate.getTime()) {
                if (!isOwner) {
                    throw new BusinessException(BusinessException.ACCESS_DENIED);
                }
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                changes += "Target date -> " + sdf.format(targetDate) + "\n";
                request.setTargetDate(targetDate);
            }
        }
        if (StringUtils.isNotBlank(comment)) {
            createComment(requestId, comment);
        }
        request.setLastUpdateTime(new Timestamp(System.currentTimeMillis()));
        request.setLastUpdateBy(user);
        if (request.getCategory().getName().equalsIgnoreCase(rd360Category)) {
            notificationService.notifyUpdateRd360Request(request, changes, comment, user);
        } else {
            notificationService.notifyUpdateRequest(request, changes, comment, user);
        }
        return request;
    }

    @Override
    public boolean isGuestAccess(Request request) {
        User user = getCurrentUser();
        boolean isReporter = request.getReportBy().getCsl().equals(user.getCsl());
        boolean isOwner = request.isOwner(user);
        return !isOwner && !isReporter;
    }

    private User getCurrentUser() {
        UserContext uCtx = UserContext.getInstance();
        if(null == uCtx){

        }
        return (User) dao.retrieve(User.class, uCtx.getUser().getCsl());
    }


}
